package indi.mozping.util;

import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.util.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * @author by mozping
 * @Classname SignatureUtils
 * @Description TODO
 * @Date 2020/9/15 20:30
 */
public class SignatureUtils {

    private final static String CHARSET_UTF8 = "utf8";
    private final static String ALGORITHM = "UTF-8";
    private final static String SEPARATOR = "&";


    public static String canonicalizedQueryString(Map<String, String> parameter) {
        TreeMap<String, String> sortParameter = new TreeMap<String, String>();
        sortParameter.putAll(parameter);

        StringBuilder canonicalizedQueryString = new StringBuilder();
        for (Map.Entry<String, String> entry : sortParameter.entrySet()) {
            canonicalizedQueryString.append(percentEncode(entry.getKey())).append("=")
                    .append(percentEncode(entry.getValue())).append("&");
        }
        return canonicalizedQueryString.substring(0, canonicalizedQueryString.length() - 1);
    }


    public static String percentEncode(String value) {
        try {
            return value == null ? null : URLEncoder.encode(value, CHARSET_UTF8)
                    .replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
        } catch (Exception e) {
        }
        return "";
    }

    public static byte[] hmacSHA1Signature(String secret, String baseString) throws Exception {
        if (StringUtils.isEmpty(secret) || StringUtils.isEmpty(baseString)) {
            throw new IOException("secret can not be empty");
        }
        Mac mac = Mac.getInstance("HmacSHA1");
        SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(CHARSET_UTF8), ALGORITHM);
        mac.init(keySpec);
        return mac.doFinal(baseString.getBytes(CHARSET_UTF8));
    }

    public static String newStringByBase64(byte[] bytes) throws UnsupportedEncodingException {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        return new String(Base64.encodeBase64(bytes, false), CHARSET_UTF8);
    }

    public static void main(String[] args) throws Exception {

        String url = "http://ecs.aliyuncs.com/?Timestamp=2016-02-23T12:46:24Z&Format=XML&AccessKeyId=testid&Action=DescribeRegions&SignatureMethod=HMAC-SHA1&SignatureNonce=3ee8c1b8-83d3-44af-a94f-4e0ad82fd6cf&Version=2014-05-26&SignatureVersion=1.0";

        //1.设置参数
        String AccessKeyId = "testid";
        String Format = "XML";
        String Version = "2014-05-26";
        String SignatureMethod = "HMAC-SHA1";
        String SignatureVersion = "1.0";
        String Action = "DescribeRegions";

        Map<String, String> param = new HashMap<String, String>();

        param.put("AccessKeyId", AccessKeyId);
        param.put("Format", Format);
        param.put("Version", Version);
        param.put("SignatureMethod", SignatureMethod);
        param.put("SignatureVersion", SignatureVersion);
        param.put("SignatureNonce", "3ee8c1b8-83d3-44af-a94f-4e0ad82fd6cf");
        param.put("Timestamp", "2016-02-23T12:46:24Z");
        param.put("Action", Action);

        //1.得到规范化的请求字符串，对应教程1.1
        String canonicalizedQueryString = canonicalizedQueryString(param);

        //2.得到待签名的字符串，对应教程1.2
        StringBuilder stringToSign = new StringBuilder();
        stringToSign.append("GET").append(SEPARATOR);
        stringToSign.append(percentEncode("/")).append(SEPARATOR);
        stringToSign.append(percentEncode(canonicalizedQueryString));

        //3.使用秘钥和SHA1算法对待签名的字符串计算HMAC
        byte[] signBytes = hmacSHA1Signature("testsecret" + "&", stringToSign.toString());

        //4.对HMAC进行Base64编码，得到最后的签名
        String signature = newStringByBase64(signBytes);

        //5.把前面添加到请求参数即可(略)
        System.out.println("Signature : " + signature);

    }
}